import java.util.Arrays;

public class StackImpl<T> implements Stack<T> {
    private static final int INITIAL_CAPACITY = 16;
    Object[] data;
    int size;
    int pointer = 0;

    public StackImpl(int size) {
        this.size = size;
        this.data = new Object[size];
    }

    public StackImpl(Object[] data) {
        this.data = data;
        this.size = data.length;
        this.pointer = data.length;
    }

    public StackImpl() {
        data = new Object[INITIAL_CAPACITY];
        size = INITIAL_CAPACITY;
    }

    public T pop() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty! pop() cannot be executed.");
        }
        final T popValue = (T) data[--pointer];
        data[pointer] = null;
        return popValue;
    }

    public void push(T element) {
        data[pointer++] = element;
        if (pointer > size) {
            resizeStack();
        }
    }

    @Override
    public String toString() {
        return Arrays.toString(data);
    }

    private void resizeStack() {
        size = size * 2;
        data = Arrays.copyOf(data, size);
    }

    public boolean isEmpty() {
        return pointer <= 0;
    }

    public T getElementAt(int index) {
        if (index < 0 || index > pointer) throw new ArrayIndexOutOfBoundsException("Negative index passed.");
        return (T) data[index];

    }

    public T peek() {
        return getElementAt(pointer - 1);
    }

    @Override
    public int getNumberOfElements() {
        return pointer;
    }

    /*
       3.1 Describe how you could use a single array to implement three stacks.
    */

    // Easy enough, just calculate the element position based on stack number {0, 1, 2} and its local index {0..globalSize/3}

}